package org.nf.trading.service.app.circle.impl;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageInfo;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.nf.trading.dao.CommentMapper;
import org.nf.trading.dao.CommentReplyMapper;
import org.nf.trading.dao.PostMapper;
import org.nf.trading.dto.CommentReplySecondaryDTO;
import org.nf.trading.dto.PostCommentDTO;
import org.nf.trading.dto.PostDTO;
import org.nf.trading.dto.ProductCommentDTO;
import org.nf.trading.entity.*;
import org.nf.trading.exception.app.circle.PostDataException;
import org.nf.trading.exception.app.product.FileUploadException;
import org.nf.trading.exception.app.product.ProductDataException;
import org.nf.trading.service.app.circle.LikeService;
import org.nf.trading.service.app.circle.PostService;
import org.nf.trading.service.app.circle.enums.PostCommentEnum;
import org.nf.trading.service.app.circle.enums.PostEnum;
import org.nf.trading.service.app.product.enums.ProductCommentEnum;
import org.nf.trading.service.app.product.enums.ProductEnum;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
@Slf4j
@Transactional
public class PostServiceImpl implements PostService {
    private final PostMapper postMapper;
    private final CommentMapper commentMapper;
    private final CommentReplyMapper commentReplyMapper;
    private final RedisTemplate<String,Object> redisTemplate;
    private final MinioClient minioClient;
    private final LikeService likeService;


    /**
     * 通过用户id查询帖子的数量
     * @param uid
     * @return
     */
    @Override
    public Long countPostByUid(Integer uid) {
        if (uid == null){
            throw new PostDataException(500,"用户id为空");
        }
        return postMapper.countPostByUid(uid);
    }

    @Override
    public List<PostDTO> listPosts(Integer pageNum, Integer pageSize) {
        ArrayList<PostDTO> postDTOS = new ArrayList<>();
        List<Post> posts = postMapper.selectAll(pageNum, pageSize);
        //后期使用缓存，页数作为键
        posts.forEach((post) -> {
            PostDTO postDTO = new PostDTO();
            postDTO.setPost(post);
            postDTOS.add(postDTO);
        });
        return postDTOS;
    }

    /**
     * 通过帖子id查询一个帖子(包含用户信息和评论信息)
     * @param postId
     * @return
     */
    @Override
    public PostCommentDTO getPostById(Integer postId) {
//        if (postId == null){
//            log.error("参数postId为NULL");
//            throw new PostDataException(500,"参数postId为NULL");
//        }
//        //判断缓存中是否存在
//        if (Boolean.TRUE.equals(redisTemplate.hasKey(PostEnum.PREFIX.value() + postId))){
//            System.out.println("缓存中获取");
//            return (PostCommentDTO) redisTemplate.opsForValue().get(PostEnum.PREFIX.value() + postId);
//        }
//        //从数据库查询帖子
//        Post post = postMapper.selectByPrimaryKey(postId);
//        //从数据库查询评论
//        List<Comment> commentList = commentMapper.listCommentsByPostId(postId);
//        commentList.forEach((comment -> {
//            comment.getCommentReplies().forEach(commentReply -> {
//                Integer commentId = commentReply.getCommentId();
//                User user = commentReplyMapper.getCommentReplyUserById(commentId).getUser();
//                commentReply.setUser(user);
//            });
//        }));
//
//
//        PostCommentDTO postCommentDTO = new PostCommentDTO(post,
//                commentList);
//        //重新放回到缓存
//        redisTemplate.opsForValue().set(PostEnum.PREFIX.value() + postId,postCommentDTO);
//        return postCommentDTO;
        return getPostById2(postId);
    }

    public PostCommentDTO getPostById2(Integer postId) {
        if (postId == null) {
            log.error("异常信息：id为空");
            throw new PostDataException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "id为空");
        }
        PostCommentDTO postCommentDTO = new PostCommentDTO();
        //判断缓存中是否存在帖子
        if (Boolean.FALSE.equals(redisTemplate.hasKey(PostEnum.PREFIX.value() + postId))) {
            //获取帖子信息
            Post post = postMapper.selectByPrimaryKey(postId);
            postCommentDTO.setPost(post);
            //重新放到缓存中
            redisTemplate.opsForValue().set(PostEnum.PREFIX.value() + postId, post);
            redisTemplate.expire(PostEnum.PREFIX.value() + postId, 30, TimeUnit.MINUTES);
        }
        //判断缓存中是否存在评论
        if (Boolean.FALSE.equals(redisTemplate.hasKey(PostCommentEnum.PREFIX.value() + postId))) {
            //数据库查询帖子评论
            List<Comment> commentList = commentMapper.getCommentsByPostId(postId);
            encapsulationCommentReplySecondary(commentList, postId);
            //重新放到缓存中
            redisTemplate.opsForValue().set(PostCommentEnum.PREFIX.value() + postId, commentList);
            redisTemplate.expire(PostCommentEnum.PREFIX.value() + postId, 30, TimeUnit.MINUTES);
        }
        //缓存中获取帖子信息
        postCommentDTO.setPost((Post) redisTemplate.opsForValue().get(PostEnum.PREFIX.value() + postId));
        //缓存中获取帖子评论
        postCommentDTO.setCommentList((List<Comment>) redisTemplate.opsForValue()
                .get(PostCommentEnum.PREFIX.value() + postId));
        log.info("从缓存中获取商品信息");
        return postCommentDTO;
    }
    /**
     * 封装评论
     *
     * @param commentList
     */
    private void encapsulationCommentReplySecondary(List<Comment> commentList, Integer postId) {
        //获取评论回复中的user（评论者）
        commentList.forEach((comment -> {
            //定义数组
            List<CommentReplySecondaryDTO> commentReplySecondaryDTOList = new ArrayList<>();
            List<CommentReply> commentReplies = commentReplyMapper.listAllByCommentId(comment.getCommentId());
            commentReplies.forEach(commentReply -> {
                //创建dto保存
                CommentReplySecondaryDTO secondaryDTO = new CommentReplySecondaryDTO();
                //拿到评论回复的id
                Integer commentReplyId = commentReply.getCommentReplyId();
                //先拿到评论的回复
                CommentReply commentReplyUserById = commentReplyMapper.getCommentReplyUserById(commentReplyId);
                //拿到评论回复的评论id
                Integer secondary = commentReply.getCommentReplySecondary();
                CommentReply commentReplyById = commentReplyMapper.getCommentReplyUserById(secondary);
                //判断评论回复是否为空
                if (secondary == null) {
                    //保存回复的用户
                    secondaryDTO.setCommentator(null);
                } else {
                    //保存回复的用户
                    secondaryDTO.setCommentator(commentReplyById.getUser());
                }
                //保存评论者
                secondaryDTO.setUser(commentReplyUserById.getUser());
                secondaryDTO.setCommentReplyId(commentReplyUserById.getCommentReplyId());
                //保存评论信息
                secondaryDTO.setContent(commentReply.getCommentReplyContent());

                //保存到集合中
                commentReplySecondaryDTOList.add(secondaryDTO);
            });
            System.out.println("Arrays.toString" + Arrays.toString(commentReplySecondaryDTOList.toArray()));
            comment.setSecondaryDTO(commentReplySecondaryDTOList);
        }));
        redisTemplate.opsForValue().set(PostCommentEnum.PREFIX.value() + postId, commentList);
        redisTemplate.expire(PostCommentEnum.PREFIX.value() + postId, 20, TimeUnit.MINUTES);
    }
    /**
     * 查询全部帖子
     * @return
     */
    @Override
    public List<PostDTO> listPostsUid(Integer userId,Integer pageNum, Integer pageSize) {
//        List<Post> posts = postMapper.selectAll(pageNum, pageSize);
        //缓存的postVO集合
        List<PostDTO> postVOList = new ArrayList<>();
        postMapper.selectAll(pageNum, pageSize).forEach(post -> {
            try {
                post.setPostPageImg((String) new ObjectMapper().readValue(post.getPostImg(), List.class).get(0));
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
            PostDTO postDTO = new PostDTO();
            postDTO.setPost(post);
            //从缓存中判断当前用户是否点赞并设置到postVO中
            boolean like = likeService.isLike(post.getPostId(), userId);
            postDTO.setLike(like);
            //获取当前贴的点赞总数
            Long likeCount = likeService.likeCount(post.getPostId());
            postDTO.getPost().setPostLikes(""+likeCount);
            postVOList.add(postDTO);
        });
        return postVOList;
    }

    /**
     * 发布帖子
     * @param
     * @return
     */
    @Override
    public boolean insert(Post post, MultipartFile[] files) {
        if (post == null || files == null){
            log.error("添加失败，参数为空！");
            throw new PostDataException(500,"添加失败，参数为空！");
        }
        boolean fileUpload = fileUpload("post", files, post);

        boolean row = postMapper.insert(post) == 1;

        if (!fileUpload) {
            throw new FileUploadException(500, "文件上传失败");
        }
        log.info("主键的id是：" + post.getPostId());
        //后期加入es
        return row;
    }

    /**
     * 帖子的文件上传
     * @param path
     * @param files
     * @return
     */
    @Override
    public boolean fileUpload(String path, MultipartFile[] files,Post post) {
        List<String> list = new ArrayList<>();
        try {
            //循环遍历上传的附件
            for(MultipartFile file : files) {
                //获取文件名
                String fileName = file.getOriginalFilename();
                //保存文件名
                list.add(fileName);
                //获取文件的输入流读取文件内容
                InputStream inputStream = file.getInputStream();
                //将文件上传到minio服务器
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket("mytrading")
                        //远程上传的路径
                        .object(path + "/" + fileName)
                        //设置一个输入流，-1表示读到文件的末尾
                        .stream(inputStream, file.getSize(),-1)
                        //文件的类型
                        .contentType(file.getContentType())
                        .build());
            }
            //序列化 设置描述图片
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(list);
            post.setPostImg(json);
            System.out.println("修改后的值："+post);
        } catch (Exception e) {
            log.error("文件上传失败,错误信息为：{}",e.getMessage());
            throw new FileUploadException(500,e.getMessage());
        }
        return true;
    }
}
